package com.pic.mgt.file.io;

import java.io.File;
import java.io.IOException;

import org.apache.commons.io.FileUtils;
import org.apache.commons.io.FilenameUtils;

import com.pic.mgt.log.IgxLog;
import com.pic.mgt.utils.RunTimeSystemUtils;

/**
 * 實體檔案操作，使用 Apache Commons IO 為底層。
 * @author 洪政緯
 *
 */
public class SimpleFileOperation implements FileOperation {
	@Override
	public File moveFileToFolder(File srcFile,
							     File destFolder)
												throws IOException {
		File destFile = new File(
								FilenameUtils.concat(destFolder.getAbsolutePath(),
										srcFile.getName()));
		
		// ********************** Log **********************
		if (IgxLog.messageDetailsLog.isTraceMode()) {
			IgxLog.messageDetailsLog.trace(this,
					"Movine file from '{}' to folder '{}'.", srcFile
							.getAbsolutePath(), destFolder.getAbsolutePath());
		}
		// ********************** Log **********************
		
		try {
			// throws IOException
			FileUtils.moveFileToDirectory(srcFile, destFolder, true);
		} catch (IOException e) {
			if (e.getMessage().indexOf("already exists") >= 0) {
				// 表示目的目錄內已有相同的檔案.將之移除後再搬移
				
				// ********************** Log **********************
				if (IgxLog.messageDetailsLog.isTraceMode()) {
					IgxLog.messageDetailsLog.trace(this,
							"Deleting file '{}'.", destFile.getAbsolutePath());
				}
				// ********************** Log **********************
				
				deleteFile(destFile);
				
				// ********************** Log **********************
				if (IgxLog.messageDetailsLog.isTraceMode()) {
					IgxLog.messageDetailsLog.trace(this,
							"Deleting file '{}' completed.", destFile.getAbsolutePath());
				}
				// ********************** Log **********************
				
				FileUtils.moveFileToDirectory(srcFile, destFolder, true);
			}
			else {
				throw e;
			}
		}
		
		// ********************** Log **********************
		if (IgxLog.messageDetailsLog.isTraceMode()) {
			IgxLog.messageDetailsLog.trace(this,
					"Movine file from '{}' to folder '{}' completed.", srcFile
							.getAbsolutePath(), destFolder.getAbsolutePath());
		}
		// ********************** Log **********************
		
		return destFile;
	}
	
	@Override
	public File moveFileToFile(File srcFile, File destFile) throws IOException {
		// ********************** Log **********************
		if (IgxLog.messageDetailsLog.isTraceMode()) {
			IgxLog.messageDetailsLog.trace(this,
					"Movine file from '{}' to '{}'.",
					srcFile.getAbsolutePath(), destFile.getAbsolutePath());
		}
		// ********************** Log **********************
		
		try {
			// throws IOException
			FileUtils.moveFile(srcFile, destFile);
		} catch (IOException e) {
			if (e.getMessage().indexOf("already exists") >= 0) {
				// 表示目的目錄內已有相同的檔案.將之移除後再搬移
				
				// ********************** Log **********************
				if (IgxLog.messageDetailsLog.isTraceMode()) {
					IgxLog.messageDetailsLog.trace(this,
							"Deleting file '{}'.", destFile.getAbsolutePath());
				}
				// ********************** Log **********************
				
				deleteFile(destFile);
				
				// ********************** Log **********************
				if (IgxLog.messageDetailsLog.isTraceMode()) {
					IgxLog.messageDetailsLog.trace(this,
							"Deleting file '{}' completed.", destFile.getAbsolutePath());
				}
				// ********************** Log **********************
				
				FileUtils.moveFile(srcFile, destFile);
			}
			else {
				throw e;
			}
		}
		
		// ********************** Log **********************
		if (IgxLog.messageDetailsLog.isTraceMode()) {
			IgxLog.messageDetailsLog.trace(this,
					"Movine file from '{}' to '{}' completed.", srcFile
							.getAbsolutePath(), destFile.getAbsolutePath());
		}
		// ********************** Log **********************
		
		return destFile;
	}

	@Override
	public void copyFileToFolder(File srcFile, File destFolder) throws IOException {
		// ********************** Log **********************
		if (IgxLog.messageDetailsLog.isTraceMode()) {
			IgxLog.messageDetailsLog.trace(this,
					"Copying file from '{}' to folder '{}'.", srcFile
							.getAbsolutePath(), destFolder.getAbsolutePath());
		}
		// ********************** Log **********************
		
		/*
		 * 以下使用 while 的部份是因為, FileUtils 在複製檔案時會去檢查目標目錄是否存存,如果
		 * 不存在會自動建立，但並沒有 thread safty。也就是在二個 thread 同時複製到同一個目標
		 * 目錄時，會同時要去建立起這個目標目錄出來，此時會有一個 thread 失敗，發生IOException。
		 */
		
		byte retryCount = 0;
		while(true) {
			try {
				FileUtils.copyFileToDirectory(srcFile, destFolder);
				break;
			} catch (IOException e) {
				if (e.getMessage().indexOf("directory cannot be created") > -1) {
					/*
					 *  如果發生此種錯誤訊息,表示目標目錄無法順利建立起來.
					 *  有可能是同時間有其它 thread or process等也在建立相
					 *  同的目錄,所以等待一個 random 的時間再繼續做 
					 */
					
					if (++retryCount > 3) { // 最多重試3次
						throw e;
					}
					else {
						RunTimeSystemUtils.threadSleep(
								(int) (Math.random() * 100));
					}
				}
				else {
					throw e;
				}
			}
		}
		
		// ********************** Log **********************
		if (IgxLog.messageDetailsLog.isTraceMode()) {
			IgxLog.messageDetailsLog.trace(this,
					"Copying file from '{}' to folder '{}' completed.", srcFile
							.getAbsolutePath(), destFolder.getAbsolutePath());
		}
		// ********************** Log **********************
	}
	
	@Override
	public void copyFileToFile(File srcFile, File destFile) throws IOException {
		// ********************** Log **********************
		if (IgxLog.messageDetailsLog.isTraceMode()) {
			IgxLog.messageDetailsLog.trace(this,
					"Copying file from '{}' to '{}'.", srcFile
							.getAbsolutePath(), destFile.getAbsolutePath());
		}
		// ********************** Log **********************
		
		FileUtils.copyFile(srcFile, destFile);
		
		// ********************** Log **********************
		if (IgxLog.messageDetailsLog.isTraceMode()) {
			IgxLog.messageDetailsLog.trace(this,
					"Copying file from '{}' to '{}' completed.", srcFile
							.getAbsolutePath(), destFile.getAbsolutePath());
		}
		// ********************** Log **********************
	}
	
	@Override
	public void deleteFile(File file) throws IOException {
		// ********************** Log **********************
		if (IgxLog.messageDetailsLog.isTraceMode()) {
			IgxLog.messageDetailsLog.trace(this,
					"Deleting file '{}'.", file.getAbsolutePath());
		}
		// ********************** Log **********************
		
		FileUtils.forceDelete(file);
		
		// ********************** Log **********************
		if (IgxLog.messageDetailsLog.isTraceMode()) {
			IgxLog.messageDetailsLog.trace(this,
					"Deleting file '{}'completed.", file.getAbsolutePath());
		}
		// ********************** Log **********************
	}
	
	@Override
	/**
	 * 建立目錄，會自動建立不存在的父目錄。
	 */
	public void createFolder(File folder) throws IOException {
		// ********************** Log **********************
		if (IgxLog.messageDetailsLog.isTraceMode()) {
			IgxLog.messageDetailsLog.trace(this,
					"Creating folder '{}'.", folder.getAbsolutePath());
		}
		// ********************** Log **********************
		
		FileUtils.forceMkdir(folder);
		
		// ********************** Log **********************
		if (IgxLog.messageDetailsLog.isTraceMode()) {
			IgxLog.messageDetailsLog.trace(this,
					"Creating folder '{}' completed.", folder.getAbsolutePath());
		}
		// ********************** Log **********************
	}
}
